﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using SdmxMl.Structure;
using SdmxMl.Manager;

namespace SdmxMl.Panels
{
    public partial class DlgAddCodeRef : Form
    {
        #region Internal Members

        private HierarchyType hierarchy;
        private TreeView treeViewH;
        private Dictionary<string, CodeListType> dico;
        private bool _isFinal;

        #endregion

        public DlgAddCodeRef(HierarchyType h, TreeView tv, bool isFinal)
        {
            InitializeComponent();
            hierarchy = h;
            treeViewH = tv;
            _isFinal = isFinal;

            textBoxCode.Enabled = _isFinal == false;
            UpdateButtons();

            // Dictionary of refered codelist in hierarchy that are available in memory.
            dico = hierarchy.CodelistRefList.GetRefDictionary();

            // To select one with combobox control
            comboBoxReferences.Items.AddRange(h.CodelistRefList.ToArray());

            // Propose level to operate on based on tree depth
            ProposeLevel();
        }

        /// <summary> Propose level to operate on, based on tree depth </summary>
        public void ProposeLevel()
        {
            int index = 0;
            if (treeViewH.SelectedNode != null)
                index = treeViewH.SelectedNode.Level - 1;
            if (comboBoxReferences.Items.Count > 0 && index >= 0 && index < comboBoxReferences.Items.Count - 1)
            {
                if (comboBoxReferences.SelectedIndex == index)
                    comboBoxReferences_SelectedIndexChanged(this, EventArgs.Empty);
                else
                    comboBoxReferences.SelectedIndex = index;
            }
        }

        private void UpdateButtons()
        {
            bool sel = _isFinal == false && textBoxCode.Text.Length > 0;
            buttonAddSameLevel.Enabled = sel && treeViewH.SelectedNode != null && treeViewH.SelectedNode.Parent != null;
            buttonAddAsChild.Enabled = sel && treeViewH.SelectedNode != null;

        }

        /// <summary>Codelist reference changed, refill list of available codes</summary>
        /// <param name="sender"></param><param name="e"></param>
        private void comboBoxReferences_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Fill list with unused code on selected codelist
            listBoxCodesAvailables.Items.Clear();
            textBoxCode.Text = string.Empty;
            UpdateButtons();

            using (new SdmxMl.Helper.CWaitCursor())
            {
                CodelistRefType crt = comboBoxReferences.SelectedItem as CodelistRefType;
                if (crt != null && dico.ContainsKey(crt.Alias))
                {
                    // Add only codes not yet refered in hierarchy tree
                    CodeListType cl = dico[crt.Alias];
                    listBoxCodesAvailables.BeginUpdate();
                    foreach (CodeType c in cl.CodeList)
                        if (checkBoxAll.Checked || IsCodeRefered(treeViewH.Nodes[0], c.Id, crt.Alias) == false)
                            listBoxCodesAvailables.Items.Add(c);
                    listBoxCodesAvailables.EndUpdate();
                }
            }

        }

        /// <summary>Recursive method to determine if a code reference </summary>
        /// <param name="parentNode"></param>
        /// <param name="code"></param>
        /// <param name="clAlias"></param>
        /// <returns></returns>
        public bool IsCodeRefered(TreeNode parentNode, string code, string clAlias)
        {
            bool refered = false;
            foreach (TreeNode node in parentNode.Nodes) 
            {
                CodeRefType crt = node.Tag as CodeRefType;
                if (crt != null && crt.CodeID == code && crt.CodelistAliasRef == clAlias)
                {
                    refered = true;
                    break;
                }
                if (IsCodeRefered(node, code, clAlias) == true)
                {
                    refered = true;
                    break;
                }
            }

            return refered;
        }


        private void buttonAddSameLevel_Click(object sender, EventArgs e)
        {
            if (treeViewH.SelectedNode != null)
                AddReferences(treeViewH.SelectedNode.Parent, checkBoxAddNear.Checked);
        }

        private void buttonAddAsChild_Click(object sender, EventArgs e)
        {
            AddReferences(treeViewH.SelectedNode, treeViewH.SelectedNode != null && treeViewH.SelectedNode == treeViewH.Nodes[0] ? 
                checkBoxAddNear.Checked : false);
        }

        private void AddReferences(TreeNode parentNode, bool addNear)
        {
            if (parentNode != null)
            {
                CodelistRefType refType = comboBoxReferences.SelectedItem as CodelistRefType;
                List<CodeType> selList = new List<CodeType>(listBoxCodesAvailables.SelectedItems.Count);

                // Fill multiple selections
                if (refType != null)
                {
                    TreeNode nd = null;
                    if (listBoxCodesAvailables.SelectedItems.Count > 1)
                    {
                        foreach (CodeType c in listBoxCodesAvailables.SelectedItems)
                        {
                            CodeRefType cr = new CodeRefType();
                            cr.CodeID = c.Id;
                            cr.CodelistAliasRef = refType.Alias;

                            nd = new TreeNode(c.ToString());
                            nd.Tag = cr;
                            if (addNear)
                            {
                                parentNode.Nodes.Insert(parentNode.Nodes.IndexOf(treeViewH.SelectedNode) + 1, nd);
                            }
                            else
                            {
                                parentNode.Nodes.Add(nd);
                            }
                            selList.Add(c);
                        }

                        parentNode.Expand();
                        parentNode.Nodes[parentNode.Nodes.IndexOf(nd)].EnsureVisible();

                        listBoxCodesAvailables.ClearSelected();
                        foreach (CodeType c in selList)
                            listBoxCodesAvailables.Items.Remove(c);
                    }

                    else if (textBoxCode.Text.Length > 0)
                    {
                        // Get codelist and check code exists
                        CodeListType clt = ReferenceManager.LocateCodelist(refType);
                        if (clt != null && clt.CodeList.LocateItem(textBoxCode.Text) != null)
                        {
                            CodeType c = clt.CodeList.LocateItem(textBoxCode.Text) as CodeType;
                            CodeRefType cr = new CodeRefType();
                            cr.CodeID = c.Id;
                            cr.CodelistAliasRef = refType.Alias;

                            nd = new TreeNode(c.ToString());
                            nd.Tag = cr;
                            if (addNear)
                            {
                                parentNode.Nodes.Insert(parentNode.Nodes.IndexOf(treeViewH.SelectedNode) + 1, nd);
                            }
                            else
                            {
                                parentNode.Nodes.Add(nd);
                            }

                            selList.Add(c);

                            parentNode.Expand();
                            parentNode.Nodes[parentNode.Nodes.IndexOf(nd)].EnsureVisible();

                            listBoxCodesAvailables.ClearSelected();

                            if (listBoxCodesAvailables.Items.Contains(c))
                                listBoxCodesAvailables.Items.Remove(c);

                        }
                        else
                        {
                            MessageBox.Show("Code " + textBoxCode.Text + " is not part of selected codelist, or codelist is unavailable!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                        }
                    }
                }
            }
        }


        private CodeRefTypeList GetRefered(bool useChild)
        {
            CodeRefTypeList crl = null;
            if (treeViewH.SelectedNode != null)
            {
                // Create first level
                if (treeViewH.SelectedNode.Parent == null)
                    crl = hierarchy.CodeRefList;
                else
                {
                    CodeRefType cr = treeViewH.SelectedNode.Tag as CodeRefType;
                    if (cr != null)
                    {
                        crl = hierarchy.CodeRefList.GetCodeRefListOfCode(cr.CodeID);
                        if (crl != null && useChild)
                            crl = cr.CodeRefList;
                    }
                }
            }
            return crl;
        }

        /// <summary>Refresh action buttons</summary>
        /// <param name="sender"></param> <param name="e"></param>
        private void listBoxCodesAvailables_SelectedIndexChanged(object sender, EventArgs e)
        {
            CodeType c = listBoxCodesAvailables.SelectedItem as CodeType;
            textBoxCode.Text = (c != null) ? c.Id : string.Empty;
        }

        private void textBoxCode_TextChanged(object sender, EventArgs e)
        {
            UpdateButtons();
        }

        private void checkBoxAll_CheckedChanged(object sender, EventArgs e)
        {
            comboBoxReferences_SelectedIndexChanged(sender, e);
        }
    }
}
